package com.bonc.util.json2;

import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.text.SimpleDateFormat;

/**
 * @author ChenZhiXiang
 * @version 1.0.0
 * @Description JSON配置
 * @createTime 2019年02月24日 12:30:00
 */
public class JsonMapper {

    private static final Logger log = LoggerFactory.getLogger(JsonMapper.class);

    //用于 Java 对象的序列化及反序列化
    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        // 对象所有字段全部列入
        objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
        // 取消默认全部转换 timestamps
        objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
        // 忽略空bean转JSON的错误
        objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);
        // 所有日期格式都统一为：yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 忽略 JSON 字符串存在，但 Java 对象中不存在对应属性的情况，防止错误
        objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 对象转换json字符串
     * @param obj 任意对象
     * @param <T> 将此方法声明为泛型方法，可传入任何对象
     * @return    json字符串
     */
    public static <T> String obj2String(T obj) {
        if (obj == null) {
            return null;
        }
        // 如果是String，则返回原对象，否则执行对象转json
        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse Object to String error:", e);
            return null;
        }
    }

    /**
     * 对象转换json字符串，并返回格式化好的json字符串
     * @param obj   任意对象
     * @param <T>   将此方法声明为泛型方法，可传入任何对象
     * @return      json字符串
     */
    public static <T> String obj2StringPretty(T obj) {
        if (obj == null) {
            return null;
        }
        // 如果是String，则返回原对象，否则执行对象转json
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse Object to String error:", e);
            return null;
        }
    }

    /**
     * json字符串转换对象
     * @param str   json字符串
     * @param clazz 转换目标对象
     * @param <T>   将此方法声明为泛型方法，可传入任何对象
     * @return <T>  目标对象
     */
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }

    /**
     * json字符串转换对象
     * @param str           json字符串
     * @param typeReference 转换目标对象
     * @param <T>           将此方法声明为泛型方法，可传入任何对象
     * @return <T>          目标对象的集合
     */
    public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isEmpty(str) || typeReference == null) {
            return null;
        }

        try {
            return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }

    /**
     * json 字符串转换成“集合对象”
     * @param str               json字符串
     * @param collectionClass   集合类型
     * @param elementClass      Java对象
     * @param <T>               将此方法声明为泛型方法，可传入任何对象
     * @return                  目标对象的集合
     */
    public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClass) {
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClass);
        try {
            return objectMapper.readValue(str, javaType);
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }
}
